/****************************************************************************
 * Copyright (c)2010 REMAIN B.V. The Netherlands. (http://www.remainsoftware.com).
 *
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 *  Contributors:
 *    Wim Jongman - initial API and implementation 
 *    Ahmed Aadel - initial API and implementation     
 *
 * SPDX-License-Identifier: EPL-2.0
 *****************************************************************************/
package org.eclipse.ecf.provider.zookeeper.core.internal;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.UUID;

import org.eclipse.core.runtime.Assert;
import org.eclipse.ecf.core.identity.ID;
import org.eclipse.ecf.provider.zookeeper.core.DefaultDiscoveryConfig;
import org.eclipse.ecf.provider.zookeeper.core.IDiscoveryConfig;
import org.eclipse.ecf.provider.zookeeper.core.ZooDiscoveryContainer.FLAVOR;
import org.eclipse.ecf.provider.zookeeper.util.Geo;
import org.eclipse.ecf.provider.zookeeper.util.Logger;
import org.osgi.framework.ServiceException;
import org.osgi.framework.ServiceReference;
import org.osgi.service.log.LogService;

public class Configuration extends DefaultDiscoveryConfig {

	private File zooConfFile;
	private File zookeeperDataFile;
	private ServiceReference reference;
	private List<String> serverIps = new ArrayList<String>();
	private FLAVOR flavor;
	private static final String LOCALHOST = "localhost";//$NON-NLS-1$

	public Configuration(ServiceReference reference) {
		Assert.isNotNull(reference);
		Set<String> legalKeys = getConfigProperties().keySet();
		for (String key : reference.getPropertyKeys()) {
			if (legalKeys.contains(key)
					|| key.startsWith(DefaultDiscoveryConfig.ZOODISCOVERY_PREFIX))
				getConfigProperties().put(key, reference.getProperty(key));
		}
	}

	public Configuration(ID targetId) {
		this(targetId.getName());
	}

	public Configuration(String propsAsString) {
		Assert.isNotNull(propsAsString);
		String ss[] = propsAsString.split(";");//$NON-NLS-1$
		for (String s : ss) {
			String key_value[] = s.split("=");//$NON-NLS-1$
			if (key_value.length == 2)
				defaultConfigProperties.put(key_value[0], key_value[1]);
		}
	}

	public Configuration configure() {
		PrintWriter writer = null;
		boolean isNewZookeeperData = false;
		try {
			String dataDirName = (String) getConfigProperties().get(
					ZOOKEEPER_DATADIR);
			// if no data directory name is specified, we randomly pick one.
			if (DATADIR_DEFAULT.equals(dataDirName)) {
				dataDirName = randomDirName();
			}
			this.zookeeperDataFile = new File(new File(getConfigProperties()
					.get(ZOOKEEPER_TEMPDIR).toString()), dataDirName);
			isNewZookeeperData = this.zookeeperDataFile.mkdir();
			this.zookeeperDataFile.deleteOnExit();
			if (!isNewZookeeperData) {
				/*
				 * the same data directory is being reused, we try emptying it
				 * to avoid data corruption
				 */
				clean();
			}
			this.zooConfFile = new File(this.zookeeperDataFile, "zoo.cfg");//$NON-NLS-1$
			this.zooConfFile.createNewFile();
			this.zooConfFile.deleteOnExit();
			if (getConfigProperties().containsKey(
					ZOODISCOVERY_FLAVOR_CENTRALIZED)) {
				this.setFlavor(FLAVOR.CENTRALIZED);
				this.serverIps = parseIps();
				if (this.serverIps.size() != 1) {
					String msg = "ZooDiscovery property "
							+ ZOODISCOVERY_FLAVOR_CENTRALIZED
							+ " must contain exactly one IP address designating the location of the ZooDiscovery instance playing this central role.";
					Logger.log(LogService.LOG_ERROR, msg, null);
					throw new ServiceException(msg);
				}

			} else if (getConfigProperties().containsKey(
					ZOODISCOVERY_FLAVOR_REPLICATED)) {
				this.setFlavor(FLAVOR.REPLICATED);
				this.serverIps = parseIps();
				if (!this.serverIps.contains(Geo.getHost())) {
					this.serverIps.add(Geo.getHost());
				}
				if (this.serverIps.size() < 2) {
					String msg = "Industrial Discovery property "//$NON-NLS-1$
							+ IDiscoveryConfig.ZOODISCOVERY_FLAVOR_REPLICATED
							+ " must contain at least one IP address which is not localhost.";
					Logger.log(LogService.LOG_ERROR, msg, null);
					throw new ServiceException(msg);
				}

			} else if (getConfigProperties().containsKey(
					ZOODISCOVERY_FLAVOR_STANDALONE)) {
				this.setFlavor(FLAVOR.STANDALONE);
				this.serverIps = parseIps();
			}
			Collections.sort(this.serverIps);
			if (this.isQuorum()) {
				String myip = Geo.getHost();
				int myId = this.serverIps.indexOf(myip);
				File myIdFile = new File(getZookeeperDataFile(), "myid");//$NON-NLS-1$
				myIdFile.createNewFile();
				myIdFile.deleteOnExit();
				writer = new PrintWriter(myIdFile);
				writer.print(myId);
				writer.flush();
				writer.close();
			}
			writer = new PrintWriter(this.zooConfFile);
			if (this.isQuorum()) {
				for (int i = 0; i < this.serverIps.size(); i++) {
					writer.println("server."//$NON-NLS-1$
							+ i + "="//$NON-NLS-1$
							+ this.serverIps.get(i) + ":"//$NON-NLS-1$
							+ getServerPort() + ":"//$NON-NLS-1$
							+ getElectionPort());
				}
			}
			for (String k : getConfigProperties().keySet()) {
				if (k.startsWith(ZOODISCOVERY_PREFIX)) {
					/*
					 * Ignore properties that are not intended for ZooKeeper
					 * internal configuration
					 */
					continue;
				}
				writer.println(k + "=" + getConfigProperties().get(k));//$NON-NLS-1$
			}
			writer.flush();
			writer.close();

		} catch (IOException e) {
			Logger.log(LogService.LOG_ERROR, e.getMessage(), e);
		} finally {
			if (writer != null)
				writer.close();
		}
		return this;
	}

	private String randomDirName() {
		String name = UUID.randomUUID() + "";
		name = name.replaceAll("-", "");
		return "zdd" + name;
	}

	public int getElectionPort() {
		return Integer.parseInt((String) getConfigProperties().get(
				ZOOKEEPER_ELECTION_PORT));
	}

	public String getConfFile() {
		return this.zooConfFile.toString();
	}

	public String getServerIps() {
		String ipsString = ""; //$NON-NLS-1$
		for (String i : this.serverIps) {
			ipsString += i + ",";//$NON-NLS-1$ //$NON-NLS-2$
		}
		return ipsString.substring(0, ipsString.lastIndexOf(","));//$NON-NLS-1$
	}

	public int getClientPort() {
		return Integer.parseInt((String) getConfigProperties().get(
				ZOOKEEPER_CLIENTPORT));
	}

	public List<String> getServerIpsAsList() {
		return this.serverIps;
	}

	public File getZookeeperDataFile() {
		return this.zookeeperDataFile;
	}

	public void setFlavor(FLAVOR flavor) {
		this.flavor = flavor;
	}

	public FLAVOR getFlavor() {
		return this.flavor;
	}

	public boolean isQuorum() {
		return this.flavor == FLAVOR.REPLICATED;
	}

	public boolean isCentralized() {
		return this.flavor == FLAVOR.CENTRALIZED;
	}

	public boolean isStandAlone() {
		return this.flavor == FLAVOR.STANDALONE;
	}

	public ServiceReference getReference() {
		return this.reference;
	}

	private void clean() {
		for (File file : this.zookeeperDataFile.listFiles()) {
			try {
				if (file.isDirectory()) {
					for (File f : file.listFiles())
						f.delete();
				}
				file.delete();
			} catch (Throwable t) {
				Logger.log(LogService.LOG_ERROR, t.getMessage(), null);
			}
		}
	}

	private List<String> parseIps() {
		List<String> ips = Arrays.asList(((String) getConfigProperties().get(
				flavor.toString())).split(","));//$NON-NLS-1$
		List<String> unfixedSize = new ArrayList<String>();
		for (String ip : ips) {
			if (ip.contains(LOCALHOST))
				ip = ip.replace(LOCALHOST, Geo.getHost());
			unfixedSize.add(ip);
		}
		Collections.sort(unfixedSize);
		return unfixedSize;
	}

	public String toString() {
		String s = flavor.name();
		for (Object o : parseIps())
			s += o;
		return s;
	}

	public int getTickTime() {
		return Integer.parseInt((String) getConfigProperties().get(
				ZOOKEEPER_TICKTIME));
	}

	public int getServerPort() {
		return Integer.parseInt((String) getConfigProperties().get(
				ZOOKEEPER_SERVER_PORT));
	}
}
